glitter makes SPARQL

glitter, un package R pour explorer et collecter des données du web sémantique

Lise Vaudor

21/10/2021

Projet: mon métier

Projet: Les données (participatives) du web

Projet: RECIT

Projet émergent ENS RECIT

=> R pour l’Exploration et la Collecte Intégrée de Triplets de données

Projet: Cas d’études

Recrutement de Camille Scheffler (stage M2) et exploration des Wikidata pour deux cas d’études:

Projet: Package glitter

En lien (et en parallèle) aux cas d’études de Camille, développement du package R glitter.

🎯 Objectifs:

Promouvoir l’usage (exploration, recueil, analyse) des LOD pour les chercheurs et étudiants usagers de R, en:

Projet: Difficultés d’appropriation des LOD

  • 👀 ce qu’on appréhende directement: le web documentaire
  • 💭 difficultés liées à la structure des données en graphes
    • visualisation souvent incomplète du graphe (modèle de données)
    • métadonnées intégrées aux données => il faut collecter les données pour comprendre ce qu’il y a dedans
    • Transformation en données tabulaires pour analyse

Projet: Difficultés de collecte

Importance des exemples de requêtes SPARQL pour explorer les bases de données

Or, le langage SPARQL est spécifique à

  • cette étape (dans l’analyse) du recueil de données
  • ce type de données (les Linked Open Data)

Projet: avancement

Un petit projet … (projet émergent ENS, 20 000 euros sur 3 ans). 🐈

Un package en cours de développement => amélioration à prévoir début 2022.

Package installable et modifiable ici.

Wikidata: exploration côté “documentaire”

Lien

Wikidata: exploration côté “données”

Passage par le Wikidata Query Service (WDQS)

Lien

Wikidata: focus sur le SPARQL endpoint (depuis le navigateur)

SELECT ?film ?filmLabel 
WHERE {
  ?film wdt:P31 wd:Q11424.
  SERVICE wikibase:label {
    bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". 
  } 
}
LIMIT 10

Wikidata: focus sur le code depuis R

query='SELECT ?film ?filmLabel 
WHERE {
  ?film wdt:P31 wd:Q11424.
  SERVICE wikibase:label {
    bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en".
  }
}'

result=purrr::quietly(WikidataQueryServiceR::query_wikidata)(query)
tib=result$r

🎯 Chaîne de traitement reproductible

Wikidata: focus sur le code depuis R, avec glitter

tib=spq_init() %>% 
  spq_add("?film wdt:P31 wd:Q11424", label="?film") %>% 
  spq_head(n=10) %>% 
  send()
film filmLabel
http://www.wikidata.org/entity/Q372 We Live in Public
http://www.wikidata.org/entity/Q593 A Gang Story
http://www.wikidata.org/entity/Q595 The Intouchables
http://www.wikidata.org/entity/Q1365 Swept Away
http://www.wikidata.org/entity/Q2201 Kick-Ass
http://www.wikidata.org/entity/Q2345 12 Angry Men

Wikidata: avant-après glitter

Avant:

query='SELECT ?film ?filmLabel WHERE {
  ?film wdt:P31 wd:Q11424.
  SERVICE wikibase:label {
    bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en".
  }
}
LIMIT 10'
result=WikidataQueryServiceR::query_wikidata(query)
tib=result$r

Après:

tib=spq_init() %>% 
  spq_add("?film wdt:P31wd:Q11424", label="?film") %>% 
  spq_head(n=10) %>% 
  send()

Ex. lieux de fiction: on peut récupérer les données

 tib=spq_init() %>%                                      # Initialise requête puis
  spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%  # Ajoute triplet "?film est une instance de film" puis
  spq_add("?film wdt:P840 ?loc", label="?loc") %>%       # Ajoute triplet "?film a pour localisation narrative ?loc (à étiqueter) puis
  spq_language("en,fr") %>%                              # Affiche les étiquettes en anglais, ou à défaut en français puis
  spq_head(n=10) %>%                                     # Sélectionne les 10 premiers résultats puis
  send()                                                 # Envoie la requête
knitr::kable(tib)
film filmLabel loc locLabel
http://www.wikidata.org/entity/Q2783956 Tarzan the Tiger http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2824081 Adanggaman http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2826159 Africa a.F.r.I.c.A http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2826193 Afrique, la parole essentielle http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2826228 Africa, How Are You With Pain? http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2891602 Batouk http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2904612 Rosolino Paternò http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2937646 Captain Phillips http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q2948643 Champagne safari http://www.wikidata.org/entity/Q15 Africa
http://www.wikidata.org/entity/Q3002671 Running Free http://www.wikidata.org/entity/Q15 Africa

Ex. lieux de fiction: pas trop de données?

Combien de films ont la localisation narrative renseignée dans Wikidata?

 tib=spq_init() %>%                                       # Initialise requête puis
  spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%   # Ajoute triplet "?film est une instance de film" puis
  spq_add("?film wdt:P840 ?loc", label="?loc") %>%        # Ajoute triplet "?film a pour localisation narrative ?loc (à étiqueter) puis
  spq_summarise(c("?n_films"="COUNT(?film)")) %>%         # Résume en comptant le nombre de films puis
  send()                                                  # Envoie la requête
knitr::kable(tib)                                         # Montre la table
n_films
46877

Ex. lieux de fiction : on va restreindre un peu (géographiquement) …

lf_1=spq_init() %>%                                         # Initialise requête puis
  spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%     # Ajoute triplet "?film est une instance de film" puis
  spq_add("?film wdt:P840 ?loc", label="?loc") %>%          # Ajoute triplet "?film a pour localisation narrative ?loc (à étiqueter) puis
  spq_add("?loc wdt:P625 ?coords",                          # Ajoute triplet "?loc a pour coordonnées spatiales ?coords" 
          within_box=list(southwest=c(3,43),                #         ... restreins pour que les coordonnées soient comprises
                          northeast=c(7,47))) %>%           #         ... dans un cadre défini par ces deux points (S-O et N-E) puis
  spq_language("fr") %>%                                    # Etiquette de préférence en français puis    
  send()                                                    # Envoie la requête

Cette table comprend 317 lignes. Voici les premières:

knitr::kable(lf_1 %>% head())
film filmLabel loc locLabel coords
http://www.wikidata.org/entity/Q86427 À bout de souffle http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)
http://www.wikidata.org/entity/Q105624 La Mémoire dans la peau http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)
http://www.wikidata.org/entity/Q151711 Marie-Jo et ses deux amours http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)
http://www.wikidata.org/entity/Q190588 Love Actually http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)
http://www.wikidata.org/entity/Q208108 Arrête-moi si tu peux http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)
http://www.wikidata.org/entity/Q215319 L’Immortel http://www.wikidata.org/entity/Q23482 Marseille Point(5.376388888 43.296666666)

Ex. lieux de fiction: et on enrichit!

lf_2=spq_init() %>%                                       # Initialise requête puis
  spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%   # Ajoute triplet "?film est une instance de film" puis
  spq_add("?film wdt:P840 ?loc", label="?loc") %>%        # Ajoute triplet "?film a pour localisation narrative ?loc (à étiqueter) puis
  spq_add("?loc wdt:P625 ?coords",                        # Ajoute triplet "?loc a pour coordonnées spatiales ?coords"
          within_box=list(southwest=c(3,43),              #           ... restreins pour que les coordonnées soient comprises
                          northeast=c(7,47)))%>%          #           ... dans un cadre défini par ces deux points (S-O et N-E) puis
  spq_add("?film wdt:P18 ?image", required=FALSE) %>%     # Ajoute triplet "?film a pour image d'illustration ?image" puis
  spq_add("?film wdt:P921 ?subject",                      # Ajoute triplet "?film a pour sujet ?subject" (info optionnelle)
          label="?subject", required=FALSE) %>%           #           ... étiquette ?subject puis
  spq_add("?film wdt:P577 ?date") %>%                     # Ajoute triplet "?film a été publié à la date ?date puis
  spq_mutate(c("?year"="year(?date)")) %>%                # Ajoute une variable ?year qui corresond à l'année de ?date puis
  spq_select("-?date") %>%                                # Enlève la variable ?date puis
  spq_language("fr,en") %>%                               # Etiquette (de préférence en français, à défaut en anglais) puis
  send()                                                  # Envoie la requête

Cette table comprend 594 lignes. Voici les premières:

knitr::kable(lf_2 %>% head())
film filmLabel loc locLabel coords image subject subjectLabel year
http://www.wikidata.org/entity/Q3226058 Le Président http://www.wikidata.org/entity/Q17005 Languedoc-Roussillon Point(3.166666666 43.666666666) NA NA NA 2010
http://www.wikidata.org/entity/Q998396 L’Emmerdeur http://www.wikidata.org/entity/Q6441 Montpellier Point(3.877230555 43.610919444) NA NA NA 1973
http://www.wikidata.org/entity/Q3226058 Le Président http://www.wikidata.org/entity/Q6441 Montpellier Point(3.877230555 43.610919444) NA NA NA 2010
http://www.wikidata.org/entity/Q98730605 Antoinette dans les Cévennes http://www.wikidata.org/entity/Q626655 Cévennes Point(3.73926389 44.42623611) NA NA NA 2020
http://www.wikidata.org/entity/Q98730605 Antoinette dans les Cévennes http://www.wikidata.org/entity/Q626655 Cévennes Point(3.73926389 44.42623611) NA NA NA 2020
http://www.wikidata.org/entity/Q65924999 Seules les bêtes http://www.wikidata.org/entity/Q12580 Lozère Point(3.6 44.333333333) NA http://www.wikidata.org/entity/Q176640 aléatoirité 2019

Ex. lieux de fiction: requête SPARQL

## 
## SELECT ?film ?filmLabel ?loc ?locLabel ?coords ?image ?subject ?subjectLabel (year(?date) AS ?year)
## WHERE{
## 
## ?film wdt:P31 wd:Q11424.
## ?film wdt:P840 ?loc.
## SERVICE wikibase:box {
## ?loc wdt:P625 ?coords.
## bd:serviceParam wikibase:cornerSouthWest 'Point(3 43)'^^geo:wktLiteral.
## bd:serviceParam wikibase:cornerNorthEast 'Point(7 47)'^^geo:wktLiteral.
## }
## OPTIONAL {?film wdt:P18 ?image.}
## OPTIONAL {?film wdt:P921 ?subject.}
## ?film wdt:P577 ?date.
## 
## SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en".}
## }

Ex. lieux de fiction: nettoyage sous R

lf_c=lf_2 %>%                                                 # Considère lf_2 puis
  select(film,ends_with("Label"),coords,image,year) %>%       # Sélectionne ces variables (dont "....Label") puis
  group_by(film,coords,image,locLabel,filmLabel) %>%          # Groupe par ces variables puis 
  summarise(subjectLabel=stringr::str_c(unique(subjectLabel), # Résume par groupe: le sujet (sur une seule ligne)   
                                        collapse=", "),       #     ... en séparant les éléments par ", "
            year=min(year),                                   #     ... et l'année comme minimum des années de sortie   
            .groups="drop")                                   # Dégroupe

Cette table comprend 315 lignes. Voici les premières:

knitr::kable(head(lf_c))
film coords image locLabel filmLabel subjectLabel year
http://www.wikidata.org/entity/Q100276888 Point(6.5 44.8) NA Alpes françaises Slalom sport de compétition, sports industry 2020
http://www.wikidata.org/entity/Q1033136 Point(5.376388888 43.296666666) http://commons.wikimedia.org/wiki/Special:FilePath/Elka%C3%AFm%20Donzelli%20Cabourg%202011.jpg Marseille La guerre est déclarée cancer 2011
http://www.wikidata.org/entity/Q105624 Point(3.156666666 46.9925) NA Nevers La Mémoire dans la peau amnésie 2002
http://www.wikidata.org/entity/Q105624 Point(5.376388888 43.296666666) NA Marseille La Mémoire dans la peau amnésie 2002
http://www.wikidata.org/entity/Q1061541 Point(5.376388888 43.296666666) NA Marseille Un prophète crime organisé 2009
http://www.wikidata.org/entity/Q1062358 Point(3.086944444 45.779722222) NA Clermont-Ferrand Quand j’étais chanteur vieillissement, industrie du spectacle, relation amoureuse, âge moyen, falling in love, human bonding, chansonnier, end of career 2006

Ex. lieux de fiction: préparation des données pour carto

Préparation d’une fenêtre “pop-up” (langage html)

lf_m =lf_c %>% 
  transform_wikidata_coords("coords") %>% 
  mutate(popup=glue::glue("<h1>{filmLabel}<a href={film} target='_blank'>🔗</a></h1>
                           <li>Lieu: {locLabel}</li>
                           <li>Année de sortie: {year}</li>")) %>% 
  mutate(popup=case_when(is.na(image)~popup,
                         !is.na(image)~glue::glue("{popup}
                                                  <img src='{image}' height='200'>"))) %>% 
  mutate(popup=case_when(is.na(subjectLabel)~popup,
                         !is.na(subjectLabel)~glue::glue("{popup}
                                                         <li>Thèmes: {subjectLabel}</li>")))

Ex. lieux de fiction: production d’une carte

library(leaflet)
# Définition d'une échelle colorée
# (en fonction de date de sortie)
pal <- colorNumeric(c("red", "green", "blue"),        
                    c(1895,1950,1970,1990,2010,2021))
# Création de la carte
map=leaflet(lf_m) %>% # déf carte
  addTiles() %>%      # ajout fond de carte
  addCircleMarkers(col=~pal(year),
                   ~lng, ~lat,
                   popup = ~popup,
                   clusterOptions = markerClusterOptions())
map

Package glitter: vue d’ensemble

Un package qui suit quelques principes du tidyverse…

Package glitter: fonctions principales

Fonctions de base:

  • spq_init() pour initier une requête
  • spq_add() pour rajouter un triplet
  • send() pour envoyer la requête

Fonctions notamment inspirées de dplyr (pour la manipulation de données)

  • spq_filter()
  • spq_select()
  • spq_arrange()
  • spq_mutate()
  • spq_group_by()
  • spq_summarise()

Package glitter: spq_filter()

FILTRER les résultats renvoyés

Articles avec “wikidata” dans le titre (en anglais):

spq_init() %>%
  spq_add("?item wdt:P31 wd:Q13442814") %>%                  # ?item est une instance d'article scientifique
  spq_add("?item rdfs:label ?itemTitle") %>%                 # ?item a pour titre ?itemTitle
  spq_filter("contains(lcase(?itemTitle),'wikidata')") %>%   # <= ce titre contient (en minuscules) "wikidata"
  spq_filter("lang(?itemTitle)='en'") %>%                    # <= on filtre pour ne garder que les titres en anglais
  spq_head(n=5) %>%
  send() %>% 
  knitr::kable()
item itemTitle
http://www.wikidata.org/entity/Q18507561 Wikidata: A Free Collaborative Knowledgebase
http://www.wikidata.org/entity/Q21503276 Utilizing the Wikidata system to improve the quality of medical content in Wikipedia in diverse languages: a pilot study
http://www.wikidata.org/entity/Q21503284 Wikidata: A platform for data integration and dissemination for the life sciences and beyond
http://www.wikidata.org/entity/Q23712646 Wikidata as a semantic framework for the Gene Wiki initiative
http://www.wikidata.org/entity/Q24074986 From Freebase to Wikidata: The Great Migration

Package glitter: spq_mutate()

MODIFIER les résultats renvoyés.

Données sur films, date de sortie ET année de sortie

spq_init() %>%
 spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%   # ?film est un instance de film
 spq_add("?film wdt:P577 ?date") %>%                     # ?film est sorti à la date ?date
 spq_mutate(c("?year"="year(?date)")) %>%                # <= ajoute variable ?year correspondant à l'année de la date
 spq_head(5) %>% 
 send() %>% 
 knitr::kable()
film filmLabel date year
http://www.wikidata.org/entity/Q46609 Una spina nel cuore 1986-01-01 1986
http://www.wikidata.org/entity/Q46637 Maximum Overdrive 1986-07-25 1986
http://www.wikidata.org/entity/Q46637 Maximum Overdrive 1986-11-20 1986
http://www.wikidata.org/entity/Q46717 Pirates of the Caribbean: The Curse of the Black Pearl 2003-07-09 2003
http://www.wikidata.org/entity/Q46717 Pirates of the Caribbean: The Curse of the Black Pearl 2003-08-13 2003

Package glitter: spq_select()

SELECTIONNER les variables renvoyées

Données sur films, localisation narrative, date de sortie ET année de sortie

spq_init() %>%
 spq_add("?film wdt:P31 wd:Q11424", label="?film") %>%   # ?film est une instance de film
 spq_add("?film wdt:P577 ?date") %>%                     # ?film est sorti à la date ?date
 spq_mutate(c("?year"="year(?date)")) %>%                # ?year est l'année correspondant à ?date
 spq_select("-?date") %>%                                # <= on retire la variable ?date
 spq_head(5) %>% 
 send() %>% 
 knitr::kable()
film filmLabel year
http://www.wikidata.org/entity/Q46609 Una spina nel cuore 1986
http://www.wikidata.org/entity/Q46637 Maximum Overdrive 1986
http://www.wikidata.org/entity/Q46637 Maximum Overdrive 1986
http://www.wikidata.org/entity/Q46717 Pirates of the Caribbean: The Curse of the Black Pearl 2003
http://www.wikidata.org/entity/Q46717 Pirates of the Caribbean: The Curse of the Black Pearl 2003

Package glitter: spq_arrange()

ORDONNER les résultats renvoyés.

Personnes nées ou habitant à New York qui font l’objet du plus grand nombre d’articles Wikimedia.

spq_init() %>%
  spq_add("?pers wdt:P31 wd:Q5",label="?item") %>%        # ?pers est une instance de personne
  spq_add("?pers wdt:P19/wdt:P131* wd:Q60") %>%           # qui est née ou est située à New-York
  spq_add("?pers wikibase:sitelinks ?sitelinks") %>%      # ?personne fait l'objet de n liens dans le projet Wikimedia
  spq_arrange(c("desc(?sitelinks)")) %>%                  # <= classe par ordre décroissant de n
  spq_head(n=5) %>%
  send() %>% 
  knitr::kable()
pers sitelinks
http://www.wikidata.org/entity/Q22686 279
http://www.wikidata.org/entity/Q4617 219
http://www.wikidata.org/entity/Q19848 178
http://www.wikidata.org/entity/Q33866 172
http://www.wikidata.org/entity/Q25089 150

Package glitter: spq_group_by, spq_summarise()

GROUPER et RESUMER les résultats renvoyés.

Communes d’Auvergne-Rhône-Alpes qui ont été supprimées lors de la réforme territoriale (nombre par département).

tib=spq_init() %>%
  spq_add("?com wdt:P31 wd:Q21869758") %>%          # ?com est une instance de commune disparue
  spq_add("?com wdt:P131* wd:Q18338206") %>%        # ?com est située dans un
  spq_add("?com wdt:P131* ?dep", label="?dep") %>% 
  spq_add("?dep wdt:P31 wd:Q6465") %>% 
  spq_group_by(c("?dep", "?depLabel")) %>% 
  spq_summarise(c("?ncomsup"="count(?com)")) %>%
  send()

Autres endpoints

tib=spq_init() %>% 
  spq_prefix(prefixes=c(dbo="http://dbpedia.org/ontology/")) %>% 
  spq_add("?person rdfs:label 'David Bowie'@en")  %>% 
  spq_add("?person dbo:birthPlace ?place") %>% 
  send("https://dbpedia.org/sparql")
tib=spq_init() %>% 
  spq_add("?film is wd:Q4699475", label="?film") %>% 
  spq_add("?film wdt:P58 ?real", label="?real") %>% 
  send

Package glitter: préfixes et endpoints

usual_prefixes
## # A tibble: 25 × 3
##    type     name     url                                    
##    <chr>    <chr>    <chr>                                  
##  1 Wikidata wd       http://www.wikidata.org/entity/        
##  2 Wikidata wdt      http://www.wikidata.org/prop/direct/   
##  3 Wikidata ps       http://www.wikidata.org/prop/statement/
##  4 Wikidata pq       http://www.wikidata.org/prop/qualifier/
##  5 Wikidata wikibase http://wikiba.se/ontology#             
##  6 generic  foaf     http://xmlns.com/foaf/0.1/             
##  7 generic  rdfs     http://www.w3.org/2000/01/rdf-schema#  
##  8 generic  bio      http://vocab.org/bio/0.1/              
##  9 generic  dcterms  http://purl.org/dc/terms/              
## 10 generic  xsd      http://www.w3.org/2001/XMLSchema#      
## # … with 15 more rows

Ex. 2: arbre généalogique de Louis XVI

people=spq_init() %>%
  spq_add("?louis16 is wd:Q7732") %>%
  spq_add("?louis16 (wdt:P22*/wdt:P25*) ?who", label="?who") %>%
  spq_select("-?louis16") %>%
  spq_language("fr") %>%
  send()%>%
  mutate(num=1:n())
list_people=people %>%
  clean_wikidata_table() %>%
  pull(who)

links=spq_init() %>%
  spq_add("?person %in% {list_people}") %>%
  spq_add("?person wdt:P22 ?p_father") %>%
  spq_add("?person wdt:P25 ?p_mother", label="?p_mother") %>% 
  spq_language("fr") %>%
  send() 

list_people=bind_rows(people,
                      links %>%
                        select(who=p_mother,whoLabel=p_motherLabel))


links=links %>%
  select(person,p_father,p_mother) %>% 
  tidyr::pivot_longer(cols=starts_with("p_"), 
                      names_to="which_parent",
                      values_to="parent")




linknew=links %>% 
  left_join(people %>%
              select(who,num_person=num),
            by=c("person"="who")) %>% 
  left_join(people %>% 
              select(who,num_parent=num),
            by=c("parent"="who"))
library(tidygraph)
library(ggraph)
tib_g=tidygraph::tbl_graph(nodes=people,
                           edges=linksnew) 
tib_g %>% ggraph(layout="sugiyama")

ANNEXES

Recrutement de Camille Scheffler (stage M2) et exploration des Wikidata pour deux cas d’études: